#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <wchar.h>
#include <dlfcn.h>
#include <limits.h>
#include <errno.h>

#include <aura.h>
#include <aura_wchar.h>


INLINE Aura_WChar * ___getbase(Aura_WChar *p, int * basep)
{
    if (p[0] == '0') {
        switch (p[1]) {
            case 'x':
                *basep = 16;
                break;
            case 't':
            case 'n':
                *basep = 10;
                break;
            case 'o':
                *basep = 8;
                break;
            default:
                *basep = 10;
                return (p);
        }
        return (p + 2);
    }
    *basep = 10;
    return (p);
}


Aura_PWChar Pwchar_t_to_Aura_PWchar(
            /* [in] */  wchar_t* wszSrc,
            /* [out] */ Aura_PWChar desc)
{
    Aura_PWChar p = desc;
    if(desc == NULL){
        return desc;
    }
    if(wszSrc == NULL){
        desc[0]=0;
        return desc;
    }
    while (*wszSrc) {
        *desc++ = (Aura_WChar)*wszSrc++;
    }
    *desc = 0;
    return p;
}

wchar_t* Aura_PWchar_to_Pwchar_t(
            /* [in] */  Aura_PWChar src,
            /* [out] */ wchar_t* wszDesc)
{
    wchar_t* p = wszDesc;
    if(wszDesc == NULL){
        return wszDesc;
    }
    if(src == NULL){
        wszDesc[0]=0;
        return wszDesc;
    }
    while (*src) {
        *wszDesc++ = (wchar_t)*src++;
    }
    *wszDesc = 0;
    return p;
}

Aura_Int32 Aura_Wcstombs(Aura_Char *s, Aura_WChar *wcs, Aura_Int32 nlen)
{
    if(wcs == NULL){
        return wcstombs(s, NULL, nlen);
    }
    Aura_Int32 len = Aura_Wcslen(wcs);
    wchar_t *p = (wchar_t*)malloc((len+1)*sizeof(wchar_t));
    if(p == NULL){
        s[0] = 0;
        return -1;
    }
    Aura_PWchar_to_Pwchar_t(wcs, p);
    Aura_Int32 ret = wcstombs(s, p, nlen);
    free (p);
    return ret;
}

Aura_Int32 Aura_Mbstowcs(Aura_WChar *wcstr, Aura_Char *mbstr, Aura_Int32 count)
{
    if(wcstr == NULL)
    {
        return mbstowcs(NULL, mbstr, count);
    }
    wchar_t *p = (wchar_t*)malloc((count+1)*sizeof(wchar_t));
    if(p == NULL){
        wcstr[0] = 0;
        return -1;
    }
    Aura_Int32 ret = mbstowcs(p, mbstr, count);
    Pwchar_t_to_Aura_PWchar(p,wcstr);
    free(p);
    return ret;
}


size_t Aura_Wcslen(
            /* [in] */  Aura_PWChar str)
{
    size_t len=0;
    while(str[len] != 0){
        len++;
    }
    return len;
}

Aura_Int32 Aura_Wcscmp(
            /* [in] */ Aura_PWChar str1,
            /* [in] */ Aura_PWChar str2,
            /* [in] */ Aura_Bool ignoreCase)
{
    Aura_WChar c1, c2;
    do {
        c1 = *str1++;
        c2 = *str2++;
        if(ignoreCase){
            if (c1 >= 'A' && c1 <= 'Z') // convert to lower case.
                c1 -= 'A' - 'a';
            if (c2 >= 'A' && c2 <= 'Z') // convert to lower case.
                c2 -= 'A' - 'a';
        }
    } while (c1 && (c1 == c2));
    return (c1 - c2);
}

Aura_Int32 Aura_Wtoi(Aura_PWChar pwszStr)
{
    Aura_Int32 v = 0;
    Aura_Int32 neg = 0;
    Aura_WChar c;

    if (!pwszStr) return 0;

    do {
        c = *pwszStr++;
    } while (isspace(c));

    if (c == '+') c = *pwszStr++;
    else if (c == '-') {
        neg = 1;
        c = *pwszStr++;
    }

    for (;;) {
        if (c < '0' || c > '9')
            break;
        v = (v * 10) + (int)(c - '0');
        c = *pwszStr++;
    }

    if(neg == 1) v = 0 - v;
    return v;
}

int Aura_Wcsncmp(Aura_PWChar pStr1,
            Aura_PWChar pStr2,
            size_t count)
{
    if (!count)
        return 0;

    while (--count && *pStr1 && *pStr1 == *pStr2) {
        pStr1++;
        pStr2++;
    }

    return *(Aura_PWChar)pStr1 - *(Aura_PWChar)pStr2;
}

EXTERN_C Aura_PWChar CDECL Aura_Wcsstr(
            Aura_PWChar string, Aura_PWChar strCharSet)
{

    if (*strCharSet != '\0') {
        Aura_WChar sub = *strCharSet++;
        size_t len = Aura_Wcslen(strCharSet);
        do {
            Aura_WChar ch;
            do {
                ch = *string++;
                if (ch == 0)
                    return NULL;
            } while (ch != sub);

            if (len == 0)
                return ((Aura_WChar *)(string-1));
        } while (Aura_Wcsncmp(string, strCharSet, len));
        string--;
    }
    return ((Aura_WChar *)string);
}


void __wstr2upper(Aura_PWChar p)
{
    if (!p)
        return;
    for (; *p; p++){
        if(*p > 'a' && *p <'z'){
            *p = *p+'A' - 'a';
        }
    }
    return;
}

EXTERN_C Aura_PWChar Aura_Wcscpy(Aura_PWChar dest, const Aura_PWChar source)
{
    register size_t i = 0;
    while (source[i]) {dest[i] = source[i]; i++;}
    dest[i] = 0;
    return dest;
}

Aura_PWChar  Aura_Wcsncpy(Aura_PWChar pDest, Aura_PWChar pSource, size_t count)
{
    Aura_PWChar p = pDest;

    while (count > 0 && (*p++= *pSource++) != 0)
        count--;

    if (count > 0)
        while(--count > 0) *p++= 0;
    return pDest;
}

Aura_PWChar __wstrichr(Aura_PWChar p, int c)
{
    Aura_PWChar           t;

    if (!p)
        return (p);
    for (t = p; *t; t++);
    for (; t >= p; t--)
        *(t + 1) = *t;
    *p = c;
    return (p);
}

Aura_PWChar Aura_Wcscat(Aura_PWChar desc, const Aura_PWChar src)
{
    Aura_PWChar d = desc;
    Aura_PWChar p = src;
    while(*d)d++;
    while(*p){
        *d++ = *p++;
    }
    *d = 0;
    return desc;
}

void __wstr_fmt(Aura_WChar * p, int size, int fmt)
{
    int             n, m, len;
    Aura_WChar wtmp[40];
    AnsiToUnic(wtmp, " ");
    len = Aura_Wcslen (p);
    switch (fmt) {
        case FMT_RJUST:
            for (n = size - len; n > 0; n--)
                __wstrichr (p, ' ');
            break;
        case FMT_LJUST:
            for (m = size - len; m > 0; m--)
                Aura_Wcscat (p, wtmp);
            break;
        case FMT_RJUST0:
            for (n = size - len; n > 0; n--)
                __wstrichr (p, '0');
            break;
        case FMT_CENTER:
            m = (size - len) / 2;
            n = size - (len + m);
            for (; m > 0; m--)
                Aura_Wcscat (p, wtmp);
            for (; n > 0; n--)
                __wstrichr (p, ' ');
            break;
    }
}


INLINE int ___atob(unsigned long *vp, Aura_WChar * p, int base)
{
    unsigned long  value, v1, v2;
    Aura_WChar           *q, tmp[20];
    int             digit;

    if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
        base = 16;
        p += 2;
    }
    if (base == 16 && (q = Aura_wcschr (p, '.')) != 0) {
        if (q - p > (Aura_Int32)sizeof(tmp) - 1)
            return (0);
        Aura_Wcsncpy (tmp, p, q - p);
        tmp[q - p] = '\0';
        if (!___atob (&v1, tmp, 16))
            return (0);
        q++;
        if (Aura_wcschr (q, '.'))
            return (0);
        if (!___atob (&v2, q, 16))
            return (0);
        *vp = (v1 << 16) + v2;
        return (1);
    }
    value = *vp = 0;
    for (; *p; p++) {
        value *= base;
        if (*p >= '0' && *p <= '9')
            digit = *p - '0';
        else if (*p >= 'a' && *p <= 'f')
            digit = *p - 'a' + 10;
        else if (*p >= 'A' && *p <= 'F')
            digit = *p - 'A' + 10;
        else
            return (0);
        if (digit >= base)
            return (0);
        value += digit;
    }
    *vp = value;
    return (1);
}


int Aura_Atob(unsigned int *vp, Aura_WChar * p, int base)
{
    unsigned long  v;
    if (base == 0)
        p = ___getbase (p, &base);
    if (___atob (&v, p, base)) {
        *vp = v;
        return (1);
    }
    return (0);
}



Aura_Int32 Aura_Wcscmp(Aura_PWChar wsza, Aura_PWChar wszb)
{
    while(*wsza == *wszb && *wsza != 0){
        wsza++;
        wszb++;
    }
    return *wsza - *wszb;
}

int Aura__Wcsnicmp(Aura_PWChar str1, Aura_PWChar str2, size_t count)
{
    Aura_WChar c1, c2;
    if (count) {
        do {
            c1 = *str1++;
            if (c1 >= 'A' && c1 <= 'Z') // convert to lower case.
                c1 -= 'A' - 'a';

            c2 = *str2++;
            if (c2 >= 'A' && c2 <= 'Z') // convert to lower case.
                c2 -= 'A' - 'a';
        } while (--count && c1 && (c1 == c2));
        return (c1 - c2);
    }
    return 0;
}


EXTERN_C void UnicToAnsi(
            /* [out] */ char *desc,
            /* [in] */ Aura_PWChar src)
{
    while (*src) {
        *desc++ = (char)*src++;
    }
    *desc = 0;
}

EXTERN_C void AnsiToUnic(
            /* [out] */ Aura_PWChar desc,
            /* [in] */ char *src)
{
    while (*src) {
        *desc++ = (Aura_WChar)*src++;
    }
    *desc = 0;
}

Aura_WChar * Aura_Wbtoa(Aura_WChar * dst, unsigned int value, int base)
{
    Aura_WChar            buf[34], wtmp[40], digit=0;
    int             i, j, rem, neg;

    if (value == 0) {
        dst[0] = '0';
        dst[1] = 0;
        return (dst);
    }
    neg = 0;
    if (base == -10) {
        base = 10;
        if (value & (1UL << 31)) {
            value = (~value) + 1;
            neg = 1;
        }
    }
    for (i = 0; value != 0; i++) {
       rem = value % base;
        value /= base;
        if (rem >= 0 && rem <= 9)
            digit = rem + '0';
        else if (rem >= 10 && rem <= 36)
            digit = (rem - 10) + 'a';
        buf[i] = digit;
    }
    buf[i] = 0;
    AnsiToUnic(wtmp, "-");
    if (neg)
        Aura_Wcscat (buf, wtmp);

/* reverse the string */
    for (i = 0, j = Aura_Wcslen (buf) - 1; j >= 0; i++, j--)
        dst[i] = buf[j];
    dst[i] = 0;
    return (dst);
}
